package pl.wtw.konnekt;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;
import org.codehaus.stax2.XMLInputFactory2;
import org.codehaus.stax2.XMLStreamReader2;

/**
 *
 * @author Adam
 */
public class BetterParser {

    /**
     * Służy do zamiany xml version
     */
    private class MyReader extends InputStream {

        InputStream is;
        boolean firstLine = true;
        boolean found = false;
        StringBuilder line = new StringBuilder();

        public MyReader(InputStream is) {
            this.is = is;
        }

        @Override
        public int read() throws IOException {
            int r = is.read();
            if (!found && firstLine) {
                //jezeli koniec pierwszej linii
                if (r=="\n".charAt(0)) {
                    firstLine = false;
                }
                byte[] b = new byte[1];
                b[0] = (byte)r;
                line.append(new String(b));
                if (line.toString().contains("version=\"1.0")) {
                    //w takim razie zamianz 1.0 na 1.1!
                    r = "1".charAt(0);
                    found = true;
                }
            }

            return r;

        }

    }

    private List<String> MY_UUID = new ArrayList<String>();
    private List<Chat> chats = null;
    private String xmlPath = "";
    private String databasePath = "";
    private boolean update = false;
    private XMLStreamReader2 xmlr;
    private XMLInputFactory2 xmlif;


    public BetterParser() {
    }

    private String log(String s) {
        StringBuilder sb = new StringBuilder();
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        Date d = new Date(System.currentTimeMillis());
        sb.append(df.format(d));
        sb.append(" ");
        sb.append(s);
        System.out.println(sb.toString());
        return sb.toString();
    }

    public void betterParse() throws XMLStreamException, ParseException, FileNotFoundException {
        

        File path = new File(xmlPath);
        File[] files;

        if (xmlPath.matches(".+[.]xml") == false) {
            files = path.listFiles(new XMLFileNameFilter());
            log("Załadowano "+files.length+" plików xml.");
        } else {
            files = new File[1];
            files[0] = path;
        }

        log("Rozpoczynam parsowanie");
        for(File f : files) {
            InputStream is = new MyReader(new BufferedInputStream((new FileInputStream(f))));
            this.xmlif = (XMLInputFactory2) XMLInputFactory2.newInstance();
            this.xmlif.configureForConvenience();
            this.xmlr = (XMLStreamReader2) xmlif.createXMLStreamReader(is);


            chats = new ArrayList<Chat>();
            Chat chat = null;
            Message msg = null;
            String currentContact=null, message=null, name=null, date=null, time=null, net=null, type=null, fromUid=null, toUid=null, tag="";
            int eventType = xmlr.getEventType();
            boolean inMessage = false, working=true;

            int count = 0;


            while (xmlr.hasNext() && working) {
                try {
                    eventType = xmlr.next();
                } catch (com.ctc.wstx.exc.WstxParsingException ex) {
                    log("Ignore next ex "+ex.toString());
                    continue;
                }
                switch (eventType) {
                    case XMLEvent.START_ELEMENT:
                        tag = xmlr.getName().toString();
                        if(tag.equals("contact"))  {
                            currentContact=xmlr.getAttributeValue(null, "name");
                            log(currentContact);
                        } else if(currentContact!=null && (tag.equals("chat") || tag.equals("dir")))  {
                            chat = new Chat(currentContact, xmlr.getAttributeValue(null, "date"), xmlr.getAttributeValue(null, "time"));
                        } else if (tag.equals("message")) {
                            inMessage = true;
                            name = xmlr.getAttributeValue(null, "name");
                            date = xmlr.getAttributeValue(null, "date");
                            time = xmlr.getAttributeValue(null, "time");
                            net = xmlr.getAttributeValue(null, "net");
                            type = xmlr.getAttributeValue(null, "type");
                            fromUid = xmlr.getAttributeValue(null, "fromUid");
                            toUid = xmlr.getAttributeValue(null, "toUid");
                        }
                        break;
                    case XMLEvent.CHARACTERS:
                        if (inMessage) {
                            try {
                                message = xmlr.getText();
                            } catch (Exception ex) {
                                log("Ignore message due to: "+ex.toString());
                                message = null;
                            }
                        }
                        break;
                    case XMLEvent.END_ELEMENT:
                        tag = xmlr.getName().toString();
                        if (tag.equals("message")) {
                            inMessage = false;
                            if (date!=null && time!=null && message!=null) {
                                msg = new Message(currentContact, name, date, time, net, type, fromUid, toUid, message, getMY_UUID_table());
                                //podmiana daty jak zła w pliku
                                if (chat==null) {
                                    chat = new Chat(currentContact, date, time);
                                    chat.addMessage(msg);
                                    count++;
                                    chats.add(chat);
                                    chat = null;
                                } else {
                                    if (msg!=null && chat!=null && (msg.getEntryTime() < chat.getStartTime())) {
                                        msg.setEntryTime(chat.getStartTime());
                                    }
                                    chat.addMessage(msg);
                                    count++;
                                }
                             }
                        } else if(tag.equals("chat") || tag.equals("dir"))  {
                            if (chat!=null && !chat.getMessages().isEmpty()) {
                                chats.add(chat);
                            }
                            chat = null;
                        }
                        break;
                    case XMLEvent.END_DOCUMENT:
                        working = false;
                        break;
                }
            }
            try {
                xmlr.close();
                addToDatabase();
                log("Koniec parsowania "+count);
            } catch (ClassNotFoundException ex) {
                log("Błąd ClassNotFoundException");
                Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
            } catch (SQLException ex) {
                log("Błąd SQLException");
                Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
            }
        }
        log("KONIEC!");
    }

    private void addToDatabase() throws ClassNotFoundException, SQLException, ParseException {
        Class.forName("org.sqlite.JDBC");
        log(databasePath);
        Connection conn =
                DriverManager.getConnection("jdbc:sqlite:" + databasePath);

        Statement stat = conn.createStatement();
        ResultSet rs = stat.executeQuery("SELECT MAX( index_id ) as maxId from wtw_chat_index");
        rs.next();
        long index = rs.getLong("maxId") + 1;

        PreparedStatement prep = conn.prepareStatement(
                "insert into wtw_chat_index (index_id, contact_id, contact_net, start_time) values (?, ?, ?, ?);");
        PreparedStatement prepMsg = conn.prepareStatement(
                "insert into wtw_chat_data (index_id, contact_id, contact_net, entry_time, entry_text, entry_flags) values (?, ?, ?, ?, ?, ?);");

        PreparedStatement prepDelChat = conn.prepareStatement(
                "DELETE FROM wtw_chat_index where start_time BETWEEN ? and ?;");
        PreparedStatement prepDelMsg = conn.prepareStatement(
                "DELETE FROM wtw_chat_data where entry_time BETWEEN ? and ?;");

        long minChatDate = Long.MAX_VALUE, maxChatDate = Long.MIN_VALUE, minMsgDate = Long.MAX_VALUE, maxMsgDate = Long.MIN_VALUE;
        log("Przygotowywanie insertów dla :" + chats.size() + " rozmów.");
        long liczbaMsg = 0;
        for (Chat c : chats) {
            if (c.getContactId() == null || c.getContactId().length() == 0) {
                throw new ParseException("Problem byl z parsowaniem, brak id. " + c, 0);
            }
            if (c.getStartTime() < minChatDate) {
                minChatDate = c.getStartTime();
            }
            if (c.getStartTime() > maxChatDate) {
                maxChatDate = c.getStartTime();
            }
            prep.setLong(1, index);
            prep.setString(2, c.getContactId());
            prep.setString(3, c.getContactNet());
            prep.setLong(4, c.getStartTime());
            prep.addBatch();

            for (Message msg : c.getMessages()) {
                if (msg.getEntryTime() < minMsgDate) {
                    minMsgDate = msg.getEntryTime();
                }
                if (msg.getEntryTime() > maxMsgDate) {
                    maxMsgDate = msg.getEntryTime();
                }
                prepMsg.setLong(1, index);
                prepMsg.setString(2, msg.getContactId());
                prepMsg.setString(3, msg.getContactNet());
                prepMsg.setLong(4, msg.getEntryTime());
                prepMsg.setString(5, msg.getEntryText());
                prepMsg.setInt(6, msg.getEntryFlags());
                prepMsg.addBatch();
                liczbaMsg++;
            }
            index++;
        }

//        conn.setAutoCommit(false);
        if (isUpdate() == true) {
            Date start = new Date(minMsgDate * 1000);
            Date end = new Date(maxMsgDate * 1000);
            DateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            log("Usuwam stare wiadomości dla: " + df.format(start) + " do " + df.format(end));
            prepDelChat.setLong(1, minChatDate);
            prepDelChat.setLong(2, maxChatDate);
            prepDelMsg.setLong(1, minMsgDate);
            prepDelMsg.setLong(2, maxMsgDate);
            prepDelChat.executeUpdate();
            prepDelMsg.executeUpdate();
        }
        log("Liczba wiadomosci do dodania: " + liczbaMsg + ". To może trochę potrwać...");
        log("To dodaję...");
        conn.setAutoCommit(false);
        prep.executeBatch();
        prepMsg.executeBatch();
        conn.commit();
        conn.close();
        log("Dodane.");
    }

    /**
     * @return the MY_UUID
     */
    public List<String> getMY_UUID() {
        return MY_UUID;
    }

    public String[] getMY_UUID_table() {
        return MY_UUID.toArray(new String[MY_UUID.size()]);
    }

    public String[] addMY_UUID(String uuid) {
        MY_UUID.add(uuid);
        return MY_UUID.toArray(new String[MY_UUID.size()]);
    }

    public String[] deleteMY_UUID(String uuid) {
        String zapamietany = null;
        for (String s : MY_UUID) {
            if (s.matches(uuid)) {
                zapamietany = s;
            }
        }
        if (zapamietany != null) {
            MY_UUID.remove(zapamietany);
        }
        return MY_UUID.toArray(new String[MY_UUID.size()]);
    }

    /**
     * @return the xmlPath
     */
    public String getXmlPath() {
        return xmlPath;
    }

    /**
     * @param xmlPath the xmlPath to set
     */
    public void setXmlPath(String xmlPath) {
        this.xmlPath = xmlPath;
    }

    /**
     * @return the databasePath
     */
    public String getDatabasePath() {
        return databasePath;
    }

    /**
     * @param databasePath the databasePath to set
     */
    public void setDatabasePath(String databasePath) {
        this.databasePath = databasePath;
    }

    /**
     * @return the update
     */
    public boolean isUpdate() {
        return update;
    }

    /**
     * @param update the update to set
     */
    public void setUpdate(boolean update) {
        this.update = update;
    }
}
